﻿using System;
using System.Globalization;
using System.Text.RegularExpressions;

namespace Kaesoft.Hospedaje.Entities
{
    #region Delegate
    public delegate bool ValidationRuleDelegate(object entity, string propertyName, object[] args);
    #endregion

    public static class ValidationRules
    {
        #region Public Methods

        public static bool IsNumber(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<string>(entity, propertyName);

            Regex pattern = new Regex(@"^[\d]{0,}$");
            return value != null && pattern.IsMatch(value.ToString());
        }

        public static bool IsTextNumber(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<string>(entity, propertyName);

            Regex pattern = new Regex(@"^[A-Za-z 0-9]{0,}$");
            return value != null && pattern.IsMatch(value.ToString());
        }

        public static bool IsText(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<string>(entity, propertyName);

            Regex pattern = new Regex(@"^[A-Za-z ]{0,}$");
            return value != null && pattern.IsMatch(value.ToString());
        }

        public static bool IsMail(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<string>(entity, propertyName);

            Regex pattern = new Regex(@"[a-z0-9]+@[a-z0-9.-]+\.[a-z]{2,4}");
            return string.IsNullOrEmpty(value) || pattern.IsMatch(value.ToString());
        }

        public static bool IsIntNotNull(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<int?>(entity, propertyName);
            return value != null;
        }

        public static bool IsPositiveShort(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<short>(entity, propertyName);
            return value > 0;
        }

        public static bool IsDateMenor(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<DateTime>(entity, propertyName);
            return value.Date < DateTime.Now.Date;
        }

        public static bool IsLessThanDate(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            if (GetPropertyValue<DateTime>(entity, propertyName) != null)
            {
                var value = GetPropertyValue<DateTime>(entity, propertyName);
                if (args.Length > 0 && args[0] != null)
                {
                    string propertyToCompare = args[0].ToString();
                    if ((GetPropertyValue<DateTime>(entity, propertyToCompare)) == null) return true;
                    var valueToCompare = GetPropertyValue<DateTime>(entity, propertyToCompare);
                    return (value.Date > valueToCompare.Date);
                }
            }
            return true;
        }

        public static bool IsLessThanDateMayorIgual(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            if (GetPropertyValue<DateTime>(entity, propertyName) != null)
            {
                var value = GetPropertyValue<DateTime>(entity, propertyName);
                if (args.Length > 0 && args[0] != null)
                {
                    string propertyToCompare = args[0].ToString();
                    if ((GetPropertyValue<DateTime>(entity, propertyToCompare)) == null) return true;
                    var valueToCompare = GetPropertyValue<DateTime>(entity, propertyToCompare);
                    return (value.Date >= valueToCompare.Date);
                }
            }
            return true;
        }

        public static bool IsBoolNotNull(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<bool?>(entity, propertyName);
            return value != null;
        }

        public static bool IsLessThan(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            if (GetPropertyValue<object>(entity, propertyName) != null)
            {
                var value = GetPropertyValue<decimal>(entity, propertyName);
                if (args.Length > 0 && args[0] != null)
                {
                    string propertyToCompare = args[0].ToString();
                    if ((GetPropertyValue<object>(entity, propertyToCompare)) == null) return true;
                    var valueToCompare = GetPropertyValue<decimal>(entity, propertyToCompare);
                    return (value < valueToCompare);
                }
            }
            return true;
        }

        public static bool IsGreaterThan(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            if (GetPropertyValue<object>(entity, propertyName) != null)
            {
                var value = GetPropertyValue<decimal>(entity, propertyName);
                if (args.Length > 0 && args[0] != null)
                {
                    string propertyToCompare = args[0].ToString();
                    if ((GetPropertyValue<object>(entity, propertyToCompare)) == null) return true;
                    var valueToCompare = GetPropertyValue<decimal>(entity, propertyToCompare);
                    return (value > valueToCompare);
                }
            }
            return true;
        }

        public static bool IsSizeThan(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            if (GetPropertyValue<string>(entity, propertyName) != null)
            {
                var value = GetPropertyValue<string>(entity, propertyName);
                int propertyToCompare = (int)args[0];
                return string.IsNullOrEmpty(value) || (value.Length > propertyToCompare);
            }
            return true;
        }

        public static bool IsGreaterThanInt(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            if (GetPropertyValue<object>(entity, propertyName) != null)
            {
                var value = GetPropertyValue<int>(entity, propertyName);
                if (args.Length > 0 && args[0] != null)
                {
                    string propertyToCompare = args[0].ToString();
                    if ((GetPropertyValue<object>(entity, propertyToCompare)) == null) return true;
                    var valueToCompare = GetPropertyValue<int>(entity, propertyToCompare);
                    return (value > valueToCompare);
                }
            }
            return true;
        }

        public static bool IsPositiveInt(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<int?>(entity, propertyName);
            return value != null && value > 0;
        }

        public static bool IsZeroPositiveInt(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<int?>(entity, propertyName);
            return value != null && value >= 0;
        }

        public static bool IsPositiveDecimal(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<decimal?>(entity, propertyName);
            return value != null && value > 0M;
        }

        public static bool IsPositiveDecimalCero(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<decimal?>(entity, propertyName);
            return value != null && value >= 0M;
        }

        public static bool IsDecimalNotNull(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<decimal?>(entity, propertyName);
            return value != null && value >= 0M;
        }

        public static bool CheckDependantProperty(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            var value = GetPropertyValue<string>(entity, propertyName);
            var used = GetPropertyValue<bool?>(entity, args[0].ToString());
            return (used.HasValue && used.Value == true && (string.IsNullOrEmpty(value))) ? false : true;
        }

        public static bool CheckIfBigger(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            var value = GetPropertyValue<decimal?>(entity, propertyName);
            var grossWeight = GetPropertyValue<decimal?>(entity, (string)args[0]);
            return (value != null && grossWeight != null && ((decimal)value > (decimal)grossWeight)) ? false : true;
        }

        public static bool StringHasSomething(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<string>(entity, propertyName);
            return !string.IsNullOrEmpty(value);
        }

        public static bool IsGuidNotEmpty(object entity, string propertyName, object[] args)
        {
            var value = GetPropertyValue<Guid>(entity, propertyName);
            return value != null && value != Guid.Empty;
        }

        public static bool IsInRangeInt(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            var value = GetPropertyValue<int>(entity, propertyName);
            var minValue = (int)args[0];
            var maxValue = (int)args[1];
            return minValue <= value && value <= maxValue;
        }

        public static bool IsInRangeDecimal(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            var value = GetPropertyValue<decimal>(entity, propertyName);
            var minValue = (decimal)args[0];
            var maxValue = (decimal)args[1];
            return minValue <= value && value <= maxValue;
        }

        public static bool DateIsBeforeOrOn(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            var value = GetPropertyValue<DateTime>(entity, propertyName);
            var maxValue = (DateTime)args[0];
            return value <= maxValue;
        }

        public static bool StringMatchesRegex(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            var value = entity.GetPropertyValue<string>(propertyName);
            var regex = new Regex((string)args[0]);
            return regex.IsMatch(value);
        }

        public static bool ValidateIfDecimalNotNull(object entity, string propertyName, object[] args)
        {
            var value = entity.GetPropertyValue<decimal?>(propertyName);
            return value.HasValue;
        }

        public static T GetPropertyValue<T>(this object entity, string propertyName)
        {
            if (entity == null) throw new ArgumentNullException("entity");
            var type = entity.GetType();
            var propertyInfo = type.GetProperty(propertyName);
            if (propertyInfo == null)
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "The class {0} doesn't have a property named {1}.", type.ToString(), propertyName));
            }
            return (T)propertyInfo.GetValue(entity, null);
        }

        public static bool IsStringIgual(object entity, string propertyName, object[] args)
        {
            if (args == null) throw new ArgumentNullException("args");
            if (GetPropertyValue<string>(entity, propertyName) != null)
            {
                var value = GetPropertyValue<string>(entity, propertyName);
                if (args.Length > 0 && args[0] != null)
                {
                    string propertyToCompare = args[0].ToString();
                    if ((GetPropertyValue<string>(entity, propertyToCompare)) == null) return true;
                    var valueToCompare = GetPropertyValue<string>(entity, propertyToCompare);
                    return (value.ToString().Trim() == valueToCompare.ToString().Trim());
                }
            }
            return true;
        }
        #endregion
    }
}